#pragma once
#include<iostream>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<pthread.h>
#include<signal.h>
#include<string>
#include<string.h>
#include"log.hpp"
#include"Task.hpp"
#include"ThreadPool.hpp"
#include"Daemon.hpp"
using namespace std;
const string defaultip="0.0.0.0";
const int defaultfd=-1;
const int backlog=10;//控制请求队列连接的长度
extern Log lg;

enum
{
    UsageError=1,
    SocketError,
    BindError,
    ListenError
};

class TcpServer;//前向声明，解决Threaddata与TcpServer的相互引用问题
class ThreadData
{
public:
    ThreadData(int fd,const string &ip,uint16_t &port,TcpServer*t)
    :sockfd(fd),clientip(ip),clientport(port),tsvr(t)
    {}
public:
    int sockfd;
    string clientip;
    uint16_t clientport;
    TcpServer *tsvr;//新线程通过该服务端指针来处理客户端连接
};
class TcpServer
{
public:
    TcpServer(const uint16_t port,const string&ip=defaultip)
    :port_(port),ip_(ip),listensock_(defaultfd)
    {}
    void InitServer()
    {
        //创建套接字
        listensock_=socket(AF_INET,SOCK_STREAM,0);
        //该接口允许端口号被重复使用，必须在负责绑定地址的监听套接字后使用
        int val = 1;
        setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR, (void*)&val, sizeof(int));
        val = 1;
        setsockopt(listensock_, SOL_SOCKET, SO_REUSEPORT, (void*)&val, sizeof(int));
        if(listensock_<0)
        {
            lg(Fatal,"create socket, errno:%d,errstring:%s",errno,strerror(errno));
            exit(BindError);
        }
        lg(Info,"create socket success, listensock_:%d,errstring: %s",errno,strerror(errno));
        //初始化网络结构体信息
        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_family=AF_INET;
        local.sin_port=htons(port_);
        local.sin_addr.s_addr=INADDR_ANY;
        //这样初始ip也行inet_aton(ip_.c_str(),&(local.sin_addr));
        //绑定套接字到地址和端口
        if(bind(listensock_,(const struct sockaddr*)&local,sizeof(local))<0)
        {
            lg(Fatal,"bind error, errno:%d,errstring:%s",errno,strerror(errno));
            exit(BindError);
        }
        lg(Info,"bind socket success, errno:%d,errstring:%s",errno,strerror(errno));
        //tcp是面向连接的，服务器一直等待连接的到来
        //服务器打开监听状态
        if(listen(listensock_,backlog)<0)
        {
            lg(Fatal,"listen error, errno:%d,errstring:%s",errno,strerror(errno));
            exit(ListenError);
        }
        lg(Info,"listen success, errno:%d,errstring:%s",errno,strerror(errno));
    }
    static void *Routine(void *args)//注意类内成员函数第一个参数默认为this指针，可能造成参数不匹配，声明为静态的就ok
    {
        pthread_detach(pthread_self());//与主线程分离，让主线程不用等待
        ThreadData*td=static_cast<ThreadData*>(args);
        td->tsvr->Service(td->sockfd,td->clientip,td->clientport);
        delete td;
        close(td->sockfd);
        return nullptr;
    }
    void Start()
    {
        Daemon();//守护进程
        lg(Info,"tcpserver is running...");
        while(true)
        {
            //1.获取新连接
            struct sockaddr_in client;
            socklen_t len=sizeof(client);
            int sockfd=accept(listensock_,(struct sockaddr*)&client,&len);
            if(sockfd<0)
            {
                lg(Warning,"accept error, errno:%d,errstring:%s",errno,strerror(errno));
                continue;
            }
            //把客户端的 “二进制、网络字节序” 的地址信息
            uint16_t clientport=ntohs(client.sin_port);
            char clientip[32];//存储网络字节序转化为点分十进制的ip
            inet_ntop(AF_INET,&(client.sin_addr),clientip,sizeof(clientip));
            //2.根据新连接来进行通信
            lg(Info,"get a new link...,sockfd:%d,client ip:%s,client port:%d",sockfd,clientip,clientport);
            //version1 单进程版
            // Service(sockfd,clientip,clientport);
            // close(sockfd);

            //version2 多进程版
            // pid_t id=fork();
            // if(id==0)//child
            // {
            //     close(listensock_);
            //     if(fork()>0) exit(0);//子进程立马退出被父进程回收(不用父进程阻塞等待)，任务交由孙子进程执行
            //     Service(sockfd,clientip,clientport);//孙子进程退出，被系统领养
            //     close(sockfd);
            //     exit(0);
            // }
            //father
            //close(sockfd);//子进程会继承父进程的文件描述符，父进程关掉不影响子进程使用，去获取新连接

            //version3 多线程版
            // ThreadData *td=new ThreadData(sockfd,(string)clientip,clientport,this);
            // pthread_t tid;
            // pthread_create(&tid,nullptr,Routine,td);//注意不能再主线程创建线程后立即关闭sockfd，否则无法使用套接字，要等线程处理完，可以在线程函数中关闭

            //version4 线程池版
            //version5 英汉词典功能也基于Task通过线程池来实现
            Task t(sockfd,clientip,clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }
    void Service(int sockfd,const string&clientip,const uint16_t&clientport)
    {
        char buffer[4096];
        while(true)
        {
            ssize_t n=read(sockfd,buffer,sizeof(buffer));
            if(n>0)
            {
                buffer[n]=0;
                cout<<"client say#"<<buffer<<endl;
                string echo_string="tcpserver echo#";
                echo_string+=buffer;
                write(sockfd,echo_string.c_str(),echo_string.size());
            }
            else if(n==0)
            {
                lg(Info,"%s:%d quit, server close sockfd: %d",clientip.c_str(),clientport,sockfd);
                break;
            }
            else{
                lg(Warning,"read error, sockfd: %d,client ip: %s,clientport: %d",sockfd,clientip.c_str(),clientport);
                break;
            }
        }
    }
    ~TcpServer(){
    }
private:
    int listensock_;
    uint16_t port_;
    string ip_;
};